//
//  MZFDFullscreenPopGesture.m
//  MZTool
//
//  Created by gby on 2023/3/1.
//

#import "MZFullscreenPopGesture.h"
#import <objc/runtime.h>





@implementation UIViewController (MZFullscreenPopGesture)

- (double )mz_interactivePopMaxAllowedInitialDistanceToLeftEdge
{
    return [objc_getAssociatedObject(self, _cmd) doubleValue];
}

- (void)setMz_interactivePopMaxAllowedInitialDistanceToLeftEdge:(double)mz_interactivePopMaxAllowedInitialDistanceToLeftEdge{
    objc_setAssociatedObject(self, @selector(mz_interactivePopMaxAllowedInitialDistanceToLeftEdge), @(mz_interactivePopMaxAllowedInitialDistanceToLeftEdge), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}


- (BOOL)mz_interactivePopDisabled
{
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setMz_interactivePopDisabled:(BOOL)disabled
{
    objc_setAssociatedObject(self, @selector(mz_interactivePopDisabled), @(disabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (BOOL)mz_prefersNavigationBarHidden
{
    return [objc_getAssociatedObject(self, _cmd) boolValue];
}

- (void)setMz_prefersNavigationBarHidden:(BOOL)hidden
{
    objc_setAssociatedObject(self, @selector(mz_prefersNavigationBarHidden), @(hidden), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end












@interface _MZFullscreenPopGestureRecognizerDelegate : NSObject <UIGestureRecognizerDelegate>

@property (nonatomic, weak) UINavigationController *navigationController;

@end

@implementation _MZFullscreenPopGestureRecognizerDelegate

- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
    if (self.navigationController.viewControllers.count <= 1) {
        return NO;
    }
    
    UIViewController *topViewController = self.navigationController.viewControllers.lastObject;
    if (topViewController.mz_interactivePopDisabled) {
        return NO;
    }

    if ([[self.navigationController valueForKey:@"_isTransitioning"] boolValue]) {
        return NO;
    }
    
    if (![gestureRecognizer isKindOfClass:UIPanGestureRecognizer.class]){
        return YES;
    }
    
    CGPoint location = [(UIPanGestureRecognizer *)gestureRecognizer locationInView:gestureRecognizer.view];
    double leftedge = topViewController.mz_interactivePopMaxAllowedInitialDistanceToLeftEdge;
    if (!(leftedge <= 0 || location.x <= leftedge)) {
        return NO;
    }
    
    CGPoint translation = [(UIPanGestureRecognizer *)gestureRecognizer translationInView:gestureRecognizer.view];
    if (translation.x <= 0) {
        return NO;
    }
    
    return YES;
}

@end

typedef void (^_MZViewControllerWillAppearInjectBlock)(UIViewController *viewController, BOOL animated);

@interface UIViewController (MZFullscreenPopGesturePrivate)

@property (nonatomic, copy) _MZViewControllerWillAppearInjectBlock mz_willAppearInjectBlock;

@end

@implementation UIViewController (MZFullscreenPopGesturePrivate)

+ (void)load
{
    Method originalMethod = class_getInstanceMethod(self, @selector(viewWillAppear:));
    Method swizzledMethod = class_getInstanceMethod(self, @selector(mz_viewWillAppear:));
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

- (void)mz_viewWillAppear:(BOOL)animated
{
    [self mz_viewWillAppear:animated];
    
    if (self.mz_willAppearInjectBlock) {
        self.mz_willAppearInjectBlock(self, animated);
    }
}

- (_MZViewControllerWillAppearInjectBlock)mz_willAppearInjectBlock
{
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setMz_willAppearInjectBlock:(_MZViewControllerWillAppearInjectBlock)block
{
    objc_setAssociatedObject(self, @selector(mz_willAppearInjectBlock), block, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

@end

@implementation UINavigationController (MZFullscreenPopGesture)

+ (void)load
{
    Method originalMethod = class_getInstanceMethod(self, @selector(pushViewController:animated:));
    Method swizzledMethod = class_getInstanceMethod(self, @selector(mz_pushViewController:animated:));
    method_exchangeImplementations(originalMethod, swizzledMethod);
}

- (void)mz_pushViewController:(UIViewController *)viewController animated:(BOOL)animated
{
    if (![self.interactivePopGestureRecognizer.view.gestureRecognizers containsObject:self.mz_fullscreenPopGestureRecognizer]) {
        
        [self.interactivePopGestureRecognizer.view addGestureRecognizer:self.mz_fullscreenPopGestureRecognizer];

        NSArray *internalTargets = [self.interactivePopGestureRecognizer valueForKey:@"targets"];
        id internalTarget = [internalTargets.firstObject valueForKey:@"target"];
        SEL internalAction = NSSelectorFromString(@"handleNavigationTransition:");
        self.mz_fullscreenPopGestureRecognizer.delegate = self.mz_popGestureRecognizerDelegate;
        [self.mz_fullscreenPopGestureRecognizer addTarget:internalTarget action:internalAction];

        self.interactivePopGestureRecognizer.enabled = NO;
    }
    
    [self mz_setupViewControllerBasedNavigationBarAppearanceIfNeeded:viewController];
    
    [self mz_pushViewController:viewController animated:animated];
}

- (void)mz_setupViewControllerBasedNavigationBarAppearanceIfNeeded:(UIViewController *)appearingViewController
{
    if (!self.mz_viewControllerBasedNavigationBarAppearanceEnabled) {
        return;
    }
    
    __weak typeof(self) weakSelf = self;
    _MZViewControllerWillAppearInjectBlock block = ^(UIViewController *viewController, BOOL animated) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (strongSelf) {
            [strongSelf setNavigationBarHidden:viewController.mz_prefersNavigationBarHidden animated:animated];
        }
    };
    
    appearingViewController.mz_willAppearInjectBlock = block;
    UIViewController *disappearingViewController = self.viewControllers.lastObject;
    if (disappearingViewController && !disappearingViewController.mz_willAppearInjectBlock) {
        disappearingViewController.mz_willAppearInjectBlock = block;
    }
}

- (_MZFullscreenPopGestureRecognizerDelegate *)mz_popGestureRecognizerDelegate
{
    _MZFullscreenPopGestureRecognizerDelegate *delegate = objc_getAssociatedObject(self, _cmd);

    if (!delegate) {
        delegate = [[_MZFullscreenPopGestureRecognizerDelegate alloc] init];
        delegate.navigationController = self;
        
        objc_setAssociatedObject(self, _cmd, delegate, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return delegate;
}

- (UIPanGestureRecognizer *)mz_fullscreenPopGestureRecognizer
{
    UIPanGestureRecognizer *panGestureRecognizer = objc_getAssociatedObject(self, _cmd);

    if (!panGestureRecognizer) {
        panGestureRecognizer = [[UIPanGestureRecognizer alloc] init];
        panGestureRecognizer.maximumNumberOfTouches = 1;
        
        objc_setAssociatedObject(self, _cmd, panGestureRecognizer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return panGestureRecognizer;
}

- (BOOL)mz_viewControllerBasedNavigationBarAppearanceEnabled
{
    NSNumber *number = objc_getAssociatedObject(self, _cmd);
    if (number) {
        return number.boolValue;
    }
    self.mz_viewControllerBasedNavigationBarAppearanceEnabled = YES;
    return YES;
}

- (void)setMz_viewControllerBasedNavigationBarAppearanceEnabled:(BOOL)enabled
{
    SEL key = @selector(mz_viewControllerBasedNavigationBarAppearanceEnabled);
    objc_setAssociatedObject(self, key, @(enabled), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

@end


